using System;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Collections;
using Unity.Burst;
using AOT;
using System.Runtime.CompilerServices;
using static Unity.Collections.AllocatorManager;
using UnityEngine;

namespace AnimCollections {

    /*
     * 通过 IAllocator 申请空间会慢一些，AllocatorManager 里的步骤比较慢
     */

    public unsafe struct CurveBuffer<T> : IDisposable where T : unmanaged, IComparable<T> {
        public struct Writer<T> where T : unmanaged, IComparable<T> {
            public readonly uint version;
            private T* listBufferPtr;
            CurveBuffer<T>* buffer;
            NativeArray<T> fixedArray;
            public int count { get; private set; }

            public Writer(uint version, ref CurveBuffer<T> buffer) {
                this.version = version;
                listBufferPtr = buffer.tailList.Ptr;
                this.buffer = (CurveBuffer<T>*)UnsafeUtility.AddressOf(ref buffer);
                fixedArray = default;
                count = 0;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public bool CanAdd() {
                return buffer->writerVersion == version;
            }

            public void Add(in T value) {
                //Debug.Assert(CanAdd(), "[CurveBuffer] CurveBufferAllocator version not match!");
                if (CanAdd()) {
                    buffer->tailList.Add(value);
                    buffer->tailList.Sort();
                    listBufferPtr = buffer->tailList.Ptr;
                    ++count;
                }
            }

            // 
            public void Fix() {
                if (fixedArray.IsCreated) return;
                if (CanAdd()) {
                    buffer->FixTailList(out fixedArray);
                } else if (count != 0) {
                    fixedArray = new NativeArray<T>();
                    fixedArray.m_AllocatorLabel = Allocator.None;
                    fixedArray.m_Buffer = listBufferPtr;
                    fixedArray.m_Length = count;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                    fixedArray.m_MinIndex = 0;
                    fixedArray.m_MaxIndex = count - 1;
#endif
                }
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public T* GetArrayPtr() {
                //Debug.Assert(fixedArray.IsCreated, "[CurveBuffer] fixedArray not created!");
                return (T*)fixedArray.m_Buffer;
            }

            // 用另一个覆盖自己
            public void OverrideFusion(ref Writer<T> other) {
                fixedArray = other.fixedArray;
                count = other.count;
                listBufferPtr = other.listBufferPtr;
            }

            internal void BuildNewArray(byte* ptr, int capacity) {
                listBufferPtr = (T*)ptr;
                count = capacity;

                fixedArray = new NativeArray<T>();
                fixedArray.m_AllocatorLabel = Allocator.None;
                fixedArray.m_Buffer = listBufferPtr;
                fixedArray.m_Length = count;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                fixedArray.m_MinIndex = 0;
                fixedArray.m_MaxIndex = count - 1;
#endif
            }
        }


        AllocatorHelper<CurveBufferAllocator> allocatorHelper;
        ref CurveBufferAllocator allocator => ref allocatorHelper.Allocator;

        UnsafeList<T> tailList;
        uint writerVersion;
        int sizeofT;
        int alignofT;
        private bool IsValid;

        public void Init() {
            sizeofT = UnsafeUtility.SizeOf<T>();
            alignofT = UnsafeUtility.AlignOf<T>();
            allocatorHelper = new AllocatorHelper<CurveBufferAllocator>(Allocator.Persistent);
            allocator.Initialize();
            writerVersion = 0;
            IsValid = true;
        }

        public void Reset() {
            tailList.Dispose();
            allocator.Reset();
        }

        public void Dispose() {
            if (IsValid) {
                tailList.Dispose();
                allocator.Dispose();
                allocatorHelper.Dispose();
                IsValid = false;
            }
        }

        public Writer<T> GetWriter() {
            if (!tailList.IsCreated) {
                BuildTailList();
            } else if (tailList.Length != 0) {
                allocator.FixDynamicBuffer(tailList.Length * sizeofT);
                BuildTailList();
            }
            ++writerVersion;
            return new Writer<T>(writerVersion, ref this);
        }

        public void CheckSpace(ref Writer<T> a, in Writer<T> b, int capacity, void* resultPtr) {
            // 最好能确定二者在内存上是否连续
            var size = capacity * sizeofT;
            allocator.CheckSpace((byte*)a.GetArrayPtr(), (byte*)b.GetArrayPtr(), size, out byte* ptr);

            UnsafeUtility.MemCpy(ptr, resultPtr, size);
            a.BuildNewArray(ptr, capacity);
        }

        public UnsafeArrayReader<T> DumpAllData() {
            allocator.DumpAllData(alignofT, out byte* buffer, out long size);

            var length = (int)size / sizeofT;
            var array = new UnsafeArrayReader<T>((T*)buffer, length, Allocator.Temp);

            //            NativeArray<T> array = new NativeArray<T>();
            //            array.m_Buffer = buffer;
            //            array.m_Length = length;
            //            array.m_AllocatorLabel = Allocator.Temp;
            //#if ENABLE_UNITY_COLLECTIONS_CHECKS
            //            array.m_MinIndex = 0;
            //            array.m_MaxIndex = length - 1;
            //            AtomicSafetyHandle.CreateHandle(out array.m_Safety, Allocator.Temp);
            //#endif
            return array;
        }

        bool FixTailList<T>(out NativeArray<T> array) where T : unmanaged {
            if (tailList.Length != 0) {
                array = new NativeArray<T>();
                array.m_AllocatorLabel = Allocator.None;
                var length = tailList.Length;
                array.m_Buffer = tailList.Ptr;
                array.m_Length = length;
#if ENABLE_UNITY_COLLECTIONS_CHECKS
                array.m_MinIndex = 0;
                array.m_MaxIndex = length - 1;
#endif
                //Debug.Log($"[CurveBuffer] List 转为 Array， 新增 length = {lenth}");
                allocator.FixDynamicBuffer(length * sizeofT);
                tailList.Dispose();
                //BuildTailList();
                //++writerVersion;
                return true;
            }
            array = default;
            //++writerVersion;
            //Debug.Log($"[CurveBuffer] 尝试 List 转为 Array， 但因为没有内容所以没做");
            return false;
        }

        void BuildTailList() {
            tailList = new UnsafeList<T>();
            int capacity = allocator.GetCurrentBlockFreeBytes() / sizeofT;
            //Debug.Log($"[CurveBuffer] BuildTailList, 期望容量为 {capacity}");
            capacity = capacity >= 8 ? capacity : 8; // 参数：最小分配容量8
            tailList.Ptr = (T*)allocator.Allocate(sizeofT, alignofT, capacity);
            tailList.m_length = 0;
            tailList.m_capacity = capacity;
            tailList.Allocator = Allocator.None;
        }
    }


    [BurstCompile]
    internal unsafe struct CurveBufferAllocator : AllocatorManager.IAllocator {
        public struct InitOption {
            public const ushort defaultBlockSize = 512; // 16字节的数据能存32个
            public const ushort maxBlockSize = 4 * 1024; // 256个
            public const int align = 16;
        }

        struct Block {
            public int blockIndex;
            public byte* data;
            public Block* next;
            public Block* prev;
            public int size; // 总字节数
            public int fixedBytes; // 下一个可被分配的位置
            public int dynamicBytes;

            public bool Contains(void* ptr) {
                return ptr >= data && ptr < data + size;
            }
        }

        public AllocatorManager.TryFunction Function => Try;
        public Allocator ToAllocator => handle.ToAllocator;
        public bool IsCustomAllocator => handle.IsCustomAllocator;
        public AllocatorManager.AllocatorHandle Handle {
            get => handle;
            set => handle = value;
        }
        AllocatorManager.AllocatorHandle handle;

        Block* headBlock;
        Block* tailBlock;
        Block* currentBlock;
        int blockCount;

        public void Initialize() {
            handle = Allocator.Persistent;
            headBlock = (Block*)Memory.Unmanaged.Allocate(sizeof(Block), UnsafeUtility.AlignOf<Block>(), Allocator.Persistent);
            *headBlock = default;
            tailBlock = headBlock;
            blockCount = 0;

            AllocateBlock(headBlock, InitOption.defaultBlockSize);
            currentBlock = headBlock;
        }

        public void Reset() {
            var curBlock = headBlock;
            do {
                curBlock->fixedBytes = 0;
                curBlock->dynamicBytes = 0;
                curBlock = curBlock->next;
            } while (curBlock != null);
        }

        public void Dispose() {
            if (tailBlock != null) {
                do {
                    var prev = tailBlock->prev;
                    Memory.Unmanaged.Free(tailBlock->data, Allocator.Persistent);
                    Memory.Unmanaged.Free(tailBlock, Allocator.Persistent);
                    tailBlock = prev;
                } while (tailBlock != null);
            }
            blockCount = 0;
            headBlock = null;
            tailBlock = null;
            handle.Dispose();
        }

        [BurstCompile]
        [MonoPInvokeCallback(typeof(AllocatorManager.TryFunction))]
        internal static int Try(IntPtr state, ref AllocatorManager.Block block) {
            unsafe { return ((CurveBufferAllocator*)state)->Try(ref block); }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool HasDynamicBuffer() {
            return 0 != currentBlock->dynamicBytes;
        }

        public int Try(ref AllocatorManager.Block block) {
            // todo:处理resize的情况：已经存在动态列表，但也要分配下一个（扩容）
            if (0 != currentBlock->dynamicBytes) {
                Debug.LogError("[CurveBuffer] 当前已经存在动态列表，必须先固定才能分配下一个");
                return -1;
            }

            return TryInternal(ref block);
        }

        int TryInternal(ref AllocatorManager.Block block) {
            if (block.Range.Pointer == IntPtr.Zero) {
                while (block.Bytes + currentBlock->fixedBytes > currentBlock->size) {
                    if (currentBlock->next == null) {
                        //Debug.Log($"[CurveBuffer] 执行了 try() 且需要分配新block:");
                        AddNewBlock((int)block.Bytes);
                        currentBlock = tailBlock;
                    } else {
                        currentBlock = currentBlock->next;
                    }
                }

                block.Range.Pointer = new(currentBlock->data + currentBlock->fixedBytes);
                currentBlock->dynamicBytes = (int)block.Bytes;
                block.AllocatedItems = block.Range.Items;
                //Debug.Log($"[CurveBuffer] 执行了 try() 且没有分配新block");
                return AllocatorManager.kErrorNone;
            }
            if (block.Range.Items == 0) {
                //Debug.Log($"[CurveBuffer] 执行了 try() 尝试释放");
            }
            return -1;
        }

        internal int GetCurrentBlockFreeBytes() {
            return currentBlock->size - currentBlock->fixedBytes;
        }

        internal void CheckSpace(byte* ptrA, byte* ptrB, int capacity, out byte* newPtr) {
            //若连续，则空间一定够用
            //若不联续（跨block的情况），则检查是否够用，够用就用，不够用，就在下一个block上拿。此时若other不是最新就完蛋了
            var isContainsA = currentBlock->Contains(ptrA);
            var isContainsB = currentBlock->Contains(ptrB); //这个必须为true
            //Debug.Assert(isContainsB, "[CurveBuffer] 当前块不包含最新的writter");

            if (isContainsA) {
                var newFixedBytes = (int)(ptrA - currentBlock->data);
                var newFreeBytes = currentBlock->size - newFixedBytes;
                //Debug.Assert(newFreeBytes >= capacity, "[CurveBuffer] 容量有误");
                currentBlock->fixedBytes = newFixedBytes + capacity;
                //currentBlock->dynamicBytes = 0;//应该已经是0
                newPtr = ptrA;
                return;
            } else {
                var prevBlock = currentBlock->prev;
                //Debug.Assert(prevBlock != null, "[CurveBuffer] 总不能找不到吧");
                var prevContainsA = prevBlock->Contains(ptrA);
                //Debug.Assert(prevContainsA, "[CurveBuffer] prevContainsA 不为true就很异常");
                var newFixedBytes = (int)(ptrA - prevBlock->data);
                var newFreeBytes = prevBlock->size - newFixedBytes;
                if (newFreeBytes >= capacity) {
                    prevBlock->fixedBytes = newFixedBytes + capacity;
                    currentBlock = prevBlock;
                    newPtr = ptrA;
                    return;
                } else {
                    //Debug.Assert(ptrB == currentBlock->data, "[CurveBuffer] B应该是从块的起始放的");
                    prevBlock->fixedBytes = newFixedBytes;
                    currentBlock->fixedBytes = capacity;
                    newPtr = ptrB;
                    return;
                }
            }
        }

        internal void FixDynamicBuffer(int lenth) {
            currentBlock->fixedBytes += lenth;
            currentBlock->dynamicBytes = 0;
            //Debug.Log($"[CurveBuffer] List 转为 Array， 到达 fixedBytes = {currentBlock->fixedBytes} , blockIndex = {currentBlock->blockIndex} ");
        }

        internal void DumpAllData(int alignOf, out byte* buffer, out long size) {
            var curBlock = headBlock;
            size = 0;
            do {
                size += curBlock->fixedBytes;
                curBlock = curBlock->next;
            } while (curBlock != null);

            buffer = (byte*)Memory.Unmanaged.Allocate(size, alignOf, Allocator.Temp);

            curBlock = headBlock;
            int addrOffset = 0;
            do {
                UnsafeUtility.MemCpy(buffer + addrOffset, curBlock->data, curBlock->fixedBytes);
                curBlock = curBlock->next;
            } while (curBlock != null);
        }

        void AddNewBlock(int inCap) {
            int newCap = inCap > tailBlock->size ? inCap : tailBlock->size; // 新块大小不小于上一个块
            //Debug.Assert(newCap <= InitOption.maxBlockSize, "newCap 超出预定最大值");

            Block* newBlock = (Block*)Memory.Unmanaged.Allocate(sizeof(Block), UnsafeUtility.AlignOf<Block>(), Allocator.Persistent);
            AllocateBlock(newBlock, newCap);
            var oldTailBlock = tailBlock;
            oldTailBlock->next = newBlock;
            newBlock->prev = oldTailBlock;
            tailBlock = newBlock;
        }

        void AllocateBlock(Block* block, int size) {
            *block = default;
            block->data = (byte*)Memory.Unmanaged.Allocate(size, 16, Allocator.Persistent); // todo:16可以看看写多少更好
            block->size = size;
            block->fixedBytes = 0;
            block->dynamicBytes = 0;
            block->blockIndex = blockCount;
            ++blockCount;
            //Debug.Log($"[CurveBuffer] 分配了新block，size = {size}, blockCount = {blockCount - 1}");
        }
    }
}
